﻿
using System;
using System.Net;
using System.Xml;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using Smrf.SocialNetworkLib;
using Smrf.AppLib;
using Smrf.XmlLib;
using Smrf.Utils;
//using Microsoft.Research.CommunityTechnologies.DateTimeLib;
using System.IO;
using System.Text;
using System.Collections;
using System.Linq;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Threading;
using System.Reflection;
using ScrewTurn.Wiki;
using ScrewTurn.Wiki.PluginFramework;


namespace Smrf.NodeXL.GraphDataProviders.MediaWiki
{
    //*****************************************************************************
    //  Class: sdcsd
    //
    /// <summary>
    /// Gets a network of Faceook friends.
    /// </summary>
    ///
    /// <remarks>
    /// Use <see cref="GetNetworkAsync" /> to asynchronously get a directed network
    /// of Facebook freinds.
    /// </remarks>
    //*****************************************************************************

    public class MediaWikiPageNetworkAnalyzer : MediWikiNetworkAnalyzerBase
    {
        //*************************************************************************
        //  Constructor: sdcsd()
        //
        /// <summary>
        /// Initializes a new instance of the <see
        /// cref="sdcsd" /> class.
        /// </summary>
        //*************************************************************************

        public MediaWikiPageNetworkAnalyzer()
        {
            // (Do nothing.)

            AssertValid();
        }

        //*************************************************************************
        //  Enum: WhatToInclude
        //
        /// <summary>
        /// Flags that specify what should be included in a network requested from
        /// this class.
        /// </summary>
        ///
        /// <remarks>
        /// The flags can be ORed together.
        /// </remarks>
        //*************************************************************************

        [System.FlagsAttribute]

        public enum
        WhatToInclude
        {
            /// <summary>
            /// Include nothing.
            /// </summary>

            None = 0,

            /// <summary>
            /// Include a vertex for each of the user's contacts.
            /// </summary>

            ContactVertices = 1,

            /// <summary>
            /// Include a vertex for each user who has commented on the user's
            /// photos.
            /// </summary>

            CommenterVertices = 2,

            /// <summary>
            /// Include information about each user in the network.
            /// </summary>

            UserInformation = 4,
        }

        private int NrOfSteps = 0;
        private int CurrentStep = 0;       
        

        //*************************************************************************
        //  Method: GetNetworkAsync()
        //
        /// <summary>
        /// Asynchronously gets a directed network of Facebook friends.
        /// </summary>
        ///
        /// <param name="s_accessToken">
        /// The access_token needed for the authentication in Facebook API.
        /// </param>
        ///
        /// <param name="includeMe">
        /// Specifies whether the ego should be included in the network.
        /// </param>
        ///
        /// <remarks>
        /// When the analysis completes, the <see
        /// cref="HttpNetworkAnalyzerBase.AnalysisCompleted" /> event fires.  The
        /// <see cref="RunWorkerCompletedEventArgs.Result" /> property will return
        /// an XmlDocument containing the network as GraphML.
        ///
        /// <para>
        /// To cancel the analysis, call <see
        /// cref="HttpNetworkAnalyzerBase.CancelAsync" />.
        /// </para>
        ///
        /// </remarks>
        //*************************************************************************

        public void
        GetNetworkAsync
        (
            string sDomain,
            string sSeedArticle,
            Dictionary<MediaWikiPageDialog.Attribute, bool> attributes,
            List<EdgeType> oEdgeTypes,
            int iNrOfRevisions,
            DateTime oDownloadRevisionsFrom
        )
        {
            Debug.Assert(!String.IsNullOrEmpty(sDomain));
            Debug.Assert(!String.IsNullOrEmpty(sSeedArticle));
            AssertValid();
            
            const String MethodName = "GetNetworkAsync";
            CheckIsBusy(MethodName);

            GetNetworkAsyncArgs oGetNetworkAsyncArgs = new GetNetworkAsyncArgs();

            oGetNetworkAsyncArgs.Domain = sDomain;
            oGetNetworkAsyncArgs.SeedArticle = sSeedArticle;
            oGetNetworkAsyncArgs.attributes = attributes;
            oGetNetworkAsyncArgs.EdgeTypes = oEdgeTypes;
            oGetNetworkAsyncArgs.NrOfRevisions = iNrOfRevisions;
            oGetNetworkAsyncArgs.DownloadRevisionsFrom = oDownloadRevisionsFrom;
            
            m_oBackgroundWorker.RunWorkerAsync(oGetNetworkAsyncArgs);
        }

        //*************************************************************************
        //  Method: GetNetwork()
        //
        /// <summary>
        /// Synchronously gets a directed network of Facebook friends.
        /// </summary>
        ///
        /// <param name="s_accessToken">
        /// The access_token needed for the authentication in Facebook API.
        /// </param>
        ///
        /// <param name="includeMe">
        /// Specifies whether the ego should be included in the network.
        /// </param>
        ///
        /// <remarks>
        /// When the analysis completes, the <see
        /// cref="HttpNetworkAnalyzerBase.AnalysisCompleted" /> event fires.  The
        /// <see cref="RunWorkerCompletedEventArgs.Result" /> property will return
        /// an XmlDocument containing the network as GraphML.
        ///
        /// <para>
        /// To cancel the analysis, call <see
        /// cref="HttpNetworkAnalyzerBase.CancelAsync" />.
        /// </para>
        ///
        /// </remarks>
        //*************************************************************************

        public XmlDocument
        GetNetwork
        (
            string sDomain,
            string sSeedArticle,
            Dictionary<MediaWikiPageDialog.Attribute, bool> attributes,
            List<EdgeType> oEdgeTypes,
            int iNrOfRevisions,
            DateTime oDownloadRevisionsFrom
        )
        {
            Debug.Assert(!String.IsNullOrEmpty(sDomain));
            Debug.Assert(!String.IsNullOrEmpty(sSeedArticle));
            AssertValid();
                     

            return (GetForumNetworkInternal(sDomain, sSeedArticle, attributes,
                                            oEdgeTypes, iNrOfRevisions, oDownloadRevisionsFrom));            
        }

        //*************************************************************************
        //  Method: GetFriendsNetworkInternal()
        //
        /// <summary>
        /// Gets the friends network from Facebook.
        /// </summary>
        ///
        /// <param name="sCookieContent">
        /// The access_token needed to execute queries in Facebook.
        /// </param>
        ///
        /// <returns>
        /// An XmlDocument containing the network as GraphML.
        /// </returns>
        //*************************************************************************

        protected XmlDocument
        GetForumNetworkInternal
        (           
            string sDomain,
            string sSeedArticle,
            Dictionary<MediaWikiPageDialog.Attribute, bool> attributes,
            List<EdgeType> oEdgeTypes,
            int iNrOfRevisions,
            DateTime oDownloadRevisionsFrom
        )

        {
            Debug.Assert(!String.IsNullOrEmpty(sDomain));
            Debug.Assert(!String.IsNullOrEmpty(sSeedArticle));                       
            AssertValid();

            Dictionary<Article, List<Dictionary<string, string>>> oArticleRevisions = new Dictionary<Article, List<Dictionary<string, string>>>();

            GraphMLXmlDocument oGraphMLXmlDocument = CreateGraphMLXmlDocument(attributes, oEdgeTypes);
            RequestStatistics oRequestStatistics = new RequestStatistics();

            m_sDomain = sDomain;
            DotNetWikiBot.Site s = new DotNetWikiBot.Site(sDomain, "", "");
            //sSeedArticle = "A.C._Milan;

            //Set the total number of steps
            NrOfSteps = 1; //Downloading Attributes
            if (oEdgeTypes.Contains(EdgeType.HyperlinkCoauthorship))
            {
                NrOfSteps += 2;
            }
            if(oEdgeTypes.Contains(EdgeType.CategoryCoauthorship))
            {
                NrOfSteps+=3;
            }

            if (oEdgeTypes.Contains(EdgeType.HyperlinkCoauthorship))
            {                
                List<string> articles = GetNonTemplateLinks(s, sSeedArticle);
                CurrentStep++;
                for (int i=0;i<articles.Count;i++)
                {
                    ReportProgress(String.Format("Step {0}/{1}: Downloading Article Revisions (Batch {2}/{3})", CurrentStep, NrOfSteps,i+1,articles.Count));
                    List<Dictionary<string, string>> revs = GetRevisions(s, articles[i], iNrOfRevisions, oDownloadRevisionsFrom);
                    if (revs.Count > 0)
                    {
                        string articleID = revs[0]["pageID"];
                        try
                        {
                            oArticleRevisions.Add(new Article(articles[i],EdgeType.HyperlinkCoauthorship), revs);
                        }
                        catch (ArgumentException e)
                        {
                            //oArticleRevisions[articleID].AddRange(revs);
                        }
                    }
                }
            }

            if (oEdgeTypes.Contains(EdgeType.CategoryCoauthorship))
            {                
                List<string> categories = GetCategories(s, sSeedArticle);
                List<string> articles = GetArticlesFromCategories(s, categories);
                CurrentStep++;
                for (int i=0;i<articles.Count;i++)
                {
                    ReportProgress(String.Format("Step {0}/{1}: Downloading Article Revisions (Batch {2}/{3})", CurrentStep, NrOfSteps,i+1,articles.Count));
                    List<Dictionary<string, string>> revs = GetRevisions(s, articles[i], iNrOfRevisions, oDownloadRevisionsFrom);
                    if (revs.Count > 0)
                    {
                        string articleID = revs[0]["pageID"];
                        try
                        {
                            oArticleRevisions.Add(new Article(articles[i], EdgeType.CategoryCoauthorship), revs);
                        }
                        catch (ArgumentException e)
                        {
                            //oArticleRevisions[articleID].AddRange(revs);
                        }
                    }
                }
            }

            Dictionary<string, Dictionary<string, string>> oAttributeValues = GetAttributes(attributes, oArticleRevisions, s);                     

            AddVertices(ref oArticleRevisions, ref oGraphMLXmlDocument, attributes, ref oAttributeValues);

            AddEdges(ref oArticleRevisions, ref oGraphMLXmlDocument);

            //After successfull download of the network
            //get the network description
            //string sNetworkDescription = "";
            //if (oForums.Count == 1)
            //{
            //    sNetworkDescription = GetNetworkDescription(oGraphMLXmlDocument,
            //                            new List<string>() { oForums.First().Value.Dictionary["name"].String },
            //                            sHost);
            //}
            //else
            //{
            //    sNetworkDescription = GetNetworkDescription(oGraphMLXmlDocument,
            //                            oForums.Keys.ToList(),
            //                            sHost);
            //}
            //OnNetworkObtainedWithoutTerminatingException(oGraphMLXmlDocument, oRequestStatistics, sNetworkDescription);

            return oGraphMLXmlDocument;
            
        }


        private List<string>
        GetNonTemplateLinks
        (
            DotNetWikiBot.Site wikiSite,
            string sArticleName
        )
        {
            //List<JSONObject> oLinks = new List<JSONObject>();
            //List<string> links = 
            return GetLinks(wikiSite, sArticleName);
        }

        private List<string>
        GetLinks
        (
            DotNetWikiBot.Site wikiSite,
            string sArticleName
        )
        {
            CurrentStep++;
            DotNetWikiBot.Page p = new DotNetWikiBot.Page(wikiSite, sArticleName);
            p.Load();
            ReportProgress(String.Format("Step {0}/{1}: Downloading Links", CurrentStep, NrOfSteps));
            return p.GetLinks().pages.Select(x => x.title).ToList();
        }

        private List<string>
        GetCategories
        (
            DotNetWikiBot.Site wikiSite,
            string sArticleName
        )
        {
            CurrentStep++;
            DotNetWikiBot.Page p = new DotNetWikiBot.Page(wikiSite, sArticleName);
            p.Load();
            ReportProgress(String.Format("Step {0}/{1}: Downloading Categories", CurrentStep, NrOfSteps));
            return p.GetCategories().ToList();
        }

        private List<string>
        GetArticlesFromCategories
        (
            DotNetWikiBot.Site wikiSite,
            List<string> oCategories
        )
        {
            CurrentStep++;
            List<string> oArticles = new List<string>();
            for (int i=0;i<oCategories.Count;i++)
            {
                ReportProgress(String.Format("Step {0}/{1}: Downloading Category Articles (Batch {2}/{3})", CurrentStep, NrOfSteps,i+1,oCategories.Count));
                oArticles.AddRange(GetCategoryMembers(wikiSite, oCategories[i]));
            }

            return oArticles;
        }

        private List<string>
        GetCategoryMembers
        (
            DotNetWikiBot.Site wikiSite,
            string sCategoryName
        )
        {
            DotNetWikiBot.PageList p = new DotNetWikiBot.PageList(wikiSite);
            p.FillFromCustomBotQueryList("categorymembers", "cmtitle="+sCategoryName+"&cmtype=page",500);

            List<string> r = p.pages.Select(x => x.title).ToList();

            return r;
        }

        private List<Dictionary<string,string>>
        GetRevisions
        (
            DotNetWikiBot.Site wikiSite,
            string sArticleName,
            int iNrOfRevisions,
            DateTime oDownloadRevisionsFrom
        )
        {
            DotNetWikiBot.PageList p = new DotNetWikiBot.PageList(wikiSite);            
            p.FillFromPageHistoryEx2(sArticleName, iNrOfRevisions, DateUtil.ConvertToTimestamp(oDownloadRevisionsFrom.Date), false);
            
            List<Dictionary<string, string>> r = p.pages.Where(x=>!String.IsNullOrEmpty(x.lastUser)).Select(x=>x.GetType()                
            .GetFields(BindingFlags.Instance | BindingFlags.Public)
           .ToDictionary(prop => prop.Name, prop=>(prop.GetValue(x)==null) ?  String.Empty : prop.GetValue(x).ToString())).ToList();
                       
            return r;
        }

        private string GetImageURL
        (
            string sPageContent,
            List<string> oImageListInArticle,
            DotNetWikiBot.Site oSite
        )
        {
            string sImageTitle = "";
            string sImageURL = "";
            foreach (string oImage in oImageListInArticle)
            {
                if (sPageContent.Contains(oImage))
                {
                    sImageTitle = oImage;
                    break;
                }
            }
            if (!String.IsNullOrEmpty(sImageTitle))
            {
                sImageURL = new DotNetWikiBot.Page(oSite).GetImageURL(sImageTitle);
            }
            return sImageURL;
        }

        private string GetShortDescription
        (
            string sPageContent
        )
        {
            return Regex.Replace(sPageContent, @"\[\[(?:[^|\]]*\|)?([^\]]+)\]\]", "");
        }

        private Dictionary<string, Dictionary<string, string>>
        GetAttributes
        (
            //LiveWorldAPI liveAPI,            
            Dictionary<MediaWikiPageDialog.Attribute, bool> attributes,
            Dictionary<Article, List<Dictionary<string, string>>> oArticleRevisions,
            DotNetWikiBot.Site oWikiSite
        )
        {
            CurrentStep++;
            Dictionary<string, Dictionary<string, string>> returnedAttributeValues = new Dictionary<string,Dictionary<string,string>>();
            DotNetWikiBot.PageList p = new DotNetWikiBot.PageList(oWikiSite);

            List<string> oVertices = oArticleRevisions.Keys.Select(x=>x.Name).ToList();

            for (int i = 0; i < oVertices.Count;i++)
            {
                ReportProgress(String.Format("Step {0}/{1}: Downloading Attributes (Batch {2}/{3})", CurrentStep, NrOfSteps, i + 1, oVertices.Count));
                p = new DotNetWikiBot.PageList(oWikiSite);
                p.FillContentFromPageHistoryEx2(oVertices[i]);
                returnedAttributeValues.Add(oVertices[i], new Dictionary<string, string>() { { "image", GetImageURL(p.pages[0].text, p.GetImages(oVertices[i]), oWikiSite) },
                                                                                             { "description", GetShortDescription(p.pages[0].text)}});
                                

            }

            List<string> oUniqueEditorsID = oArticleRevisions.Values.SelectMany(
                                            x => x.Select(y => y["lastUser"]))
                                            .Distinct().ToList();
            returnedAttributeValues = returnedAttributeValues.Union(p.GetUsersAttributes(oUniqueEditorsID)).ToDictionary(x => x.Key, x => x.Value);


            return returnedAttributeValues;
        }

        private void
        AddVertices
        (
            ref Dictionary<Article, List<Dictionary<string,string>>> oArticleRevisions,
            ref GraphMLXmlDocument oGraphMLXmlDocument,
             Dictionary<MediaWikiPageDialog.Attribute, bool> attributes,
            ref Dictionary<string, Dictionary<string, string>> oAttributeValues
        )
        {
            XmlNode oVertexXmlNode;
            List<string> oUniqueEditorsID = oArticleRevisions.Values.SelectMany(
                                            x => x.Select(y => y["lastUser"]))
                                            .Distinct().ToList();

            List<string> oUniqueVertices = new List<string>();
            oUniqueVertices.AddRange(oUniqueEditorsID);
            oUniqueVertices.AddRange(oArticleRevisions.Keys.Select(x=>x.Name));
            oUniqueVertices = oUniqueVertices.Distinct().ToList();

            foreach (string editor in oUniqueVertices)
            {
                oVertexXmlNode = oGraphMLXmlDocument.AppendVertexXmlNode(editor);
                if (oUniqueEditorsID.Contains(editor))
                {
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, "v_type", "Editor");
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, "Image", sImageURI.AbsoluteUri);
                    foreach (KeyValuePair<MediaWikiPageDialog.Attribute, bool> kvp in attributes)
                    {
                        if (kvp.Value && kvp.Key.name!="Name")
                        {
                            if (kvp.Key.value.Equals("registration"))
                            {
                                oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, kvp.Key.value,
                                   (String.IsNullOrEmpty(oAttributeValues[editor][kvp.Key.value])? "" : DateUtil.ConvertWithTimezone(oAttributeValues[editor][kvp.Key.value]).ToString()));

                            }
                            else
                            {
                                oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, kvp.Key.value,
                                    (oAttributeValues[editor][kvp.Key.value] == null ? "" : oAttributeValues[editor][kvp.Key.value]));
                            }
                        }
                    }
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, MenuTextID,
                    "Open Wiki Page for This User");
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, MenuActionID,
                        "http://"+m_sDomain+"/wiki/User:"+editor);
                }
                else
                {
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, "v_type", "Article");
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, "Image", oAttributeValues[editor]["image"]);
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, "v_content", oAttributeValues[editor]["description"]);
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, MenuTextID,
                    "Open Wiki Page for This Article");
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oVertexXmlNode, MenuActionID,
                        "http://" + m_sDomain + "/wiki/" + editor);
                }
            }
        }        

        private void
        AddEdges
        (
            ref Dictionary<Article, List<Dictionary<string, string>>> oArticleRevisions,
            ref GraphMLXmlDocument oGraphMLXmlDocument
        )
        {
            XmlNode oEdgeXmlNode;
            //Add hyperlink edges
            foreach (KeyValuePair<Article, List<Dictionary<string, string>>> kvp in oArticleRevisions.Where(x=>x.Key.Type==EdgeType.HyperlinkCoauthorship))
            {
                foreach (Dictionary<string, string> revs in kvp.Value)
                {
                    oEdgeXmlNode = oGraphMLXmlDocument.AppendEdgeXmlNode(revs["lastUser"], kvp.Key.Name);
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oEdgeXmlNode, "e_type", "Hyperlink Coauthorship");
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oEdgeXmlNode, "Relationship", "User-Article");
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oEdgeXmlNode, "e_comment", revs["comment"]);
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oEdgeXmlNode, "e_size", revs["lastBytesModified"]);
                }
            }

            //Add category edges
            foreach (KeyValuePair<Article, List<Dictionary<string, string>>> kvp in oArticleRevisions.Where(x => x.Key.Type == EdgeType.CategoryCoauthorship))
            {
                foreach (Dictionary<string, string> revs in kvp.Value)
                {
                    oEdgeXmlNode = oGraphMLXmlDocument.AppendEdgeXmlNode(revs["lastUser"], kvp.Key.Name);
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oEdgeXmlNode, "e_type", "Category Coauthorship");
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oEdgeXmlNode, "Relationship", "User-Article");
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oEdgeXmlNode, "e_comment", revs["comment"]);
                    oGraphMLXmlDocument.AppendGraphMLAttributeValue(oEdgeXmlNode, "e_size", revs["lastBytesModified"]);
                }
            }
        }

        

        //*************************************************************************
        //  Method: AppendVertexTooltipXmlNodes()
        //
        /// <summary>
        /// Appends a vertex tooltip XML node for each person in the network.
        /// </summary>
        ///
        /// <param name="oGraphMLXmlDocument">
        /// The GraphMLXmlDocument being populated.
        /// </param>
        ///
        /// <param name="oVertexXmlNode">
        /// The XmlNode representing the vertex.
        /// </param>
        /// 
        /// <param name="sVertex">
        /// The screening name of the vertex. 
        /// </param>
        /// 
        /// <param name="sDisplayString">
        /// The string to be attached after the screening name.
        /// </param>
        //*************************************************************************

        protected Dictionary<string,string>
        GetTooltips
        (            
            List<string> friendUIDs
        )
        {            
            AssertValid();

            return new Dictionary<string, string>();
        }

        

        //*************************************************************************
        //  Method: GetNetworkDescription()
        //
        /// <summary>
        /// Gets a description of the network.
        /// </summary>
        ///
        /// <param name="includeMe">
        /// A boolean value that states if the logged in user
        /// is included in the network.
        /// </param>
        /// 
        /// <param name="getWallPosts">
        /// Specifies if wall posts are downloaded.
        /// </param>
        /// 
        /// <param name="getStatusUpdates">
        /// Specifies if status updates are downloaded.
        /// </param>
        ///
        /// <param name="oGraphMLXmlDocument">
        /// The GraphMLXmlDocument that contains the network.
        /// </param>
        ///
        /// <returns>
        /// A description of the network.
        /// </returns>
        //*************************************************************************

        protected String
        GetNetworkDescription
        (            
            GraphMLXmlDocument oGraphMLXmlDocument,
            List<string> oForumsNames,
            string sHost
        )
        {            
            Debug.Assert(oGraphMLXmlDocument != null);
            AssertValid();

            NetworkDescriber oNetworkDescriber = new NetworkDescriber();

            
            oNetworkDescriber.AddSentence(
                "The graph represents the network of users who have posted in " +
                (oForumsNames.Count==1 ? "forum " : "fora ") +
                "\""+String.Join(", ", oForumsNames.ToArray())+"\". The network is downloaded " +
                "from \"" + sHost + "\"."
            );
            

            oNetworkDescriber.AddNetworkTime();

            oNetworkDescriber.AddSentence(
                    "There exists an edge from user A to user B if user A has replied to user B."
                    );

            AddPostDateRangeToNetworkDescription(oNetworkDescriber);

            return (oNetworkDescriber.ConcatenateSentences());
        }


        private void
        AddPostDateRangeToNetworkDescription
        (            
            NetworkDescriber oNetworkDescriber
        )
        {
            DateTime minPostDate = DateTime.MaxValue;
            DateTime maxPostsDate = DateTime.MinValue;

            //foreach(JSONObject message in m_oMessages){
            //    string creationDateString = message.Dictionary["creationDate"].String;
            //    DateTime tmp = DateTime.Parse(creationDateString.Substring(0,creationDateString.Length-4));

            //    if (tmp <= minPostDate)
            //    {
            //        minPostDate = tmp;
            //    }

            //    if (tmp >= maxPostsDate)
            //    {
            //        maxPostsDate = tmp;
            //    }                
            //}

            oNetworkDescriber.AddEventTime(
                "The earliest post in the network was posted ",
                minPostDate
                );

            oNetworkDescriber.AddEventTime(
                "The latest post in the network was posted ",
                maxPostsDate
                );
        }
       

        //*************************************************************************
        //  Method: GetURLs()
        //
        /// <summary>
        /// Returns the URLs found in a string
        /// </summary>
        ///
        /// <param name="txt">
        /// The text to search for URLs
        /// </param> 
        ///
        /// <param name="concatenator">
        /// The concatenator of the URLs
        /// </param>
        /// 
        /// <returns>
        /// A string with found URLs concatenated with the specified concatenator
        /// </returns>
        //*************************************************************************
        private string GetURLs(string txt, char concatenator)
        {
            Regex regx = new Regex("http://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);

            MatchCollection mactches = regx.Matches(txt);

            txt=" ";
            foreach (Match match in mactches)
            {
                txt += match.Value + concatenator;

            }

            return txt.Remove(txt.Length - 1);
        }

        //*************************************************************************
        //  Method: GetHashtags()
        //
        /// <summary>
        /// Returns the hashtags found in a string
        /// </summary>
        ///
        /// <param name="txt">
        /// The text to search for hashtags
        /// </param> 
        ///
        /// <param name="concatenator">
        /// The concatenator of the hashtags
        /// </param>
        /// 
        /// <returns>
        /// A string with found hashtags concatenated with the specified concatenator
        /// </returns>
        //*************************************************************************
        private string GetHashtags(string txt, char concatenator)
        {            
            //(#)((?:[A-Za-z0-9-_]*))
            Regex regx = new Regex("(?:(?<=\\s)|^)#(\\w*[A-Za-z_]+\\w*)", RegexOptions.IgnoreCase);

            MatchCollection mactches = regx.Matches(txt);

            txt = " ";
            foreach (Match match in mactches)
            {
                txt += match.Value + concatenator;

            }

            return txt.Remove(txt.Length - 1);
        }

        //*************************************************************************
        //  Method: CreateGraphMLXmlDocument()
        //
        /// <summary>
        /// Creates a GraphMLXmlDocument representing a network of friends in Facebook.
        /// </summary>
        ///        
        /// <returns>
        /// A GraphMLXmlDocument representing a network of Facebook friends.  The
        /// document includes GraphML-attribute definitions but no vertices or
        /// edges.
        /// </returns>
        //*************************************************************************

        protected GraphMLXmlDocument
        CreateGraphMLXmlDocument
        (
            Dictionary<MediaWikiPageDialog.Attribute, bool> attributes,
            List<EdgeType> oEdgeTypes
        )
        {
            AssertValid();

            GraphMLXmlDocument oGraphMLXmlDocument = new GraphMLXmlDocument(true);

            DefineImageFileGraphMLAttribute(oGraphMLXmlDocument);
            DefineCustomMenuGraphMLAttributes(oGraphMLXmlDocument);
            DefineRelationshipGraphMLAttribute(oGraphMLXmlDocument);
            oGraphMLXmlDocument.DefineGraphMLAttribute(false, TooltipID,
            "Tooltip", "string", null);
            oGraphMLXmlDocument.DefineGraphMLAttribute(false, "v_type", "Vertex Type", "string", "");
            oGraphMLXmlDocument.DefineGraphMLAttribute(false, "v_content", "Content", "string", "");            

            oGraphMLXmlDocument.DefineGraphMLAttribute(true, "e_weight", "Edge Weight", "int", "1");
            oGraphMLXmlDocument.DefineGraphMLAttribute(true, "e_type", "Edge Type", "string", "");
            oGraphMLXmlDocument.DefineGraphMLAttribute(true, "e_comment", "Edit Comment", "string", "");
            oGraphMLXmlDocument.DefineGraphMLAttribute(true, "e_size", "Edit Size", "string", "");


            foreach (KeyValuePair<MediaWikiPageDialog.Attribute, bool> kvp in attributes)
            {
                if (kvp.Value)
                {                    
                    oGraphMLXmlDocument.DefineGraphMLAttribute(false, kvp.Key.value,
                    kvp.Key.name, "string", null);                    
                }
            }           

            return (oGraphMLXmlDocument);
        }

        //*************************************************************************
        //  Method: WhatToIncludeFlagIsSet()
        //
        /// <summary>
        /// Checks whether a flag is set in an ORed combination of WhatToInclude
        /// flags.
        /// </summary>
        ///
        /// <param name="eORedEnumFlags">
        /// Zero or more ORed Enum flags.
        /// </param>
        ///
        /// <param name="eORedEnumFlagsToCheck">
        /// One or more Enum flags to check.
        /// </param>
        ///
        /// <returns>
        /// true if any of the <paramref name="eORedEnumFlagsToCheck" /> flags are
        /// set in <paramref name="eORedEnumFlags" />.
        /// </returns>
        //*************************************************************************

        protected Boolean
        WhatToIncludeFlagIsSet
        (
            WhatToInclude eORedEnumFlags,
            WhatToInclude eORedEnumFlagsToCheck
        )
        {
            return ((eORedEnumFlags & eORedEnumFlagsToCheck) != 0);
        }

        

        

        //*************************************************************************
        //  Method: BackgroundWorker_DoWork()
        //
        /// <summary>
        /// Handles the DoWork event on the BackgroundWorker object.
        /// </summary>
        ///
        /// <param name="sender">
        /// Source of the event.
        /// </param>
        ///
        /// <param name="e">
        /// Standard mouse event arguments.
        /// </param>
        //*************************************************************************

        protected override void
        BackgroundWorker_DoWork
        (
            object sender,
            DoWorkEventArgs e
        )
        {

            Debug.Assert(sender is BackgroundWorker);

            BackgroundWorker oBackgroundWorker = (BackgroundWorker)sender;

            Debug.Assert(e.Argument is GetNetworkAsyncArgs);

            GetNetworkAsyncArgs oGetNetworkAsyncArgs =
                (GetNetworkAsyncArgs)e.Argument;

            try
            {
                e.Result = GetForumNetworkInternal
                    (
                    oGetNetworkAsyncArgs.Domain,
                    oGetNetworkAsyncArgs.SeedArticle,                   
                    oGetNetworkAsyncArgs.attributes,
                    oGetNetworkAsyncArgs.EdgeTypes,
                    oGetNetworkAsyncArgs.NrOfRevisions,
                    oGetNetworkAsyncArgs.DownloadRevisionsFrom
                    );
            }
            catch (CancellationPendingException)
            {
                e.Cancel = true;
            }
           
        }

        private void AddKeyValue<T>(ref Dictionary<T, T> dict, List<T> keys, List<T> values)
        {
            if (keys.Count != values.Count)
            {
                throw new Exception("Keys should be the same size as values");
            }
            for (int i = 0; i < keys.Count; i++)
            {
                dict.Add(keys[i], values[i]);
            }
        }

        //*************************************************************************
        //  Method: AssertValid()
        //
        /// <summary>
        /// Asserts if the object is in an invalid state.  Debug-only.
        /// </summary>
        //*************************************************************************

        // [Conditional("DEBUG")]

        public override void
        AssertValid()
        {
            base.AssertValid();

            // (Do nothing else.)
        }

        public String txt = "";

        //*************************************************************************
        //  Protected fields
        //*************************************************************************        

        /// GraphML-attribute IDs.

        protected const String UserID = "uid";
        ///
        protected const String UserNameID = "UserName";

        private int nrOfRetries = 10;

        private string m_sHost = "";

        private string m_sCookieContent = "";

        private string m_sDomain = "";

        private static string sAssemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        private static string sImagePath = Path.Combine(sAssemblyFolder, "User.png");
        private static Uri sImageURI = new Uri(sImagePath);

        //*************************************************************************
        //  Embedded class: GetNetworkAsyncArgs()
        //
        /// <summary>
        /// Contains the arguments needed to asynchronously get a network of Flickr
        /// users.
        /// </summary>
        //*************************************************************************

        protected class GetNetworkAsyncArgs : GetNetworkAsyncArgsBase
        {
            ///
            public String Domain;
            ///
            public Dictionary<MediaWikiPageDialog.Attribute, bool> attributes;
            ///
            public String SeedArticle;   
            ///
            public List<EdgeType> EdgeTypes;
            /// 
            public int NrOfRevisions;
            /// 
            public DateTime DownloadRevisionsFrom;
        };

        private class Article
        {
            public string Name { get; set; }
            public EdgeType Type { get; set; }

            public Article(string name, EdgeType type)
            {
                Name = name;
                Type = type;
            }

            public override int GetHashCode()
            {
                return (Name.GetHashCode());
            }
            public override bool Equals(object obj)
            {
                return Equals(obj as Article);
            }
            private bool Equals(Article obj)
            {
                return (obj != null &&
                        obj.Name.Equals(this.Name));
            }
        }

        private class User
        {
            public string userID {get; set;}
            public string username  { get; set;}
        }

        private class Edge
        {
            public string Vertex1 { get; set; }
            public string Vertex2 { get; set; }
            public string Message { get; set;}
            public string Date { get; set; }
            public List<string> Messages { get; set; }
            public List<string> Dates { get; set; }
            public List<string> Forums { get; set; }

            public Edge(string Vertex1, string Vertex2, string message, string date, string forumName )
            {
                this.Vertex1 = Vertex1;
                this.Vertex2 = Vertex2;
                string tmpMessage = HtmlRemoval.StripTagsCharArray(message);
                this.Message = tmpMessage;
                this.Date = DateUtil.ConvertWithTimezone(date).ToString();
                this.Messages = new List<string>(){tmpMessage};
                this.Dates = new List<string>() { DateUtil.ConvertWithTimezone(date).ToString() };
                this.Forums = new List<string>() { HtmlRemoval.StripTagsCharArray(forumName) };
            }

            public void AddToMessage(string newMessage)
            {                
                string tmpMessage = HtmlRemoval.StripTagsCharArray(newMessage);
                if (Message.Length <= 8000)
                {
                    Message += "\n\n" + tmpMessage;
                }                
                Messages.Add(tmpMessage);
            }

            public void AddToDate(string newDate)
            {
                if (Date.Length <= 8000)
                {
                    Date += "\n\n" + DateUtil.ConvertWithTimezone(newDate).ToString();
                }
                Dates.Add(newDate);
            }

            public void AddToForums(string forumName)
            {
                Forums.Add(HtmlRemoval.StripTagsCharArray(forumName));
            }

            public override int GetHashCode()
            {
                return (Vertex1.GetHashCode()+Vertex2.GetHashCode());
            }
            public override bool Equals(object obj)
            {
                return Equals(obj as Edge);
            }
            private bool Equals(Edge obj)
            {
                return (obj != null &&
                        obj.Vertex1.Equals(this.Vertex1) &&
                        obj.Vertex2.Equals(this.Vertex2));
            }

        }

      

        

    }

}
